package zdream.pmfield.stage.buff.foe;

import java.util.Objects;
import java.util.function.Supplier;

import zdream.pmfield.data.foe.MoveAdditionalEffects;
import zdream.pmfield.data.foe.MoveTemplate;
import zdream.pmfield.stage.action.AttackAction;
import zdream.pmfield.stage.action.StageAction;
import zdream.pmfield.stage.director.AttackContext;
import zdream.pmfield.stage.director.HookType;
import zdream.pmfield.stage.director.Hooks;
import zdream.pmfield.stage.message.FoeRemoveBuffMessage;
import zdream.pmfield.stage.object.FoeActor;
import zdream.pmfield.stage.object.Seat;
import zdream.pmfield.stage.object.StageMove;

/**
 * <p>通过拥有蓄力轮次的技能 (Moves with a charging turn) 产生的 buff
 * <p>在这里, 每次进攻时会修改 {@link AttackContext#speedCostExtraRate}
 * 来保证这次等待时间比之前要长得多. 一般而言, 蓄力攻击消耗的行动点或者移动点更多,
 * 蓄力的等待时间更短, 而且下一次的轮次会更早到来.
 *
 * <p>举个例子, 比如 foe1 使用了蓄力技能 XXX, 这轮会消耗掉它所有的
 * {@link FoeActor#actionPoint} 和 {@link FoeActor#movePoint},
 * 它的下一轮本来要 0.8 round 之后, 因为蓄力的原因, 下一轮可能要等到 1.2 round 之后;
 * 另外, 蓄力技能的释放一个单独 {@link ChargingBuff}, 它产生 {@link StageAction}.
 * buff 可能会把这个 {@link StageAction} 放到 timeline 上, 发动时间是当前时间 + 0.8 round.
 * </p>
 *
 * <li>蓄力攻击结束会触发烧伤和冻伤.
 * <li>蓄力启动时不会触发烧伤和冻伤.
 * <li>蓄力状态能够被打断. 一旦被打断后, 蓄力技能的发动可能取消, 也可能提前发动. TODO
 * <li>蓄力攻击必须指定 {@link Seat}, 不能指定 {@link FoeActor} 为攻击对象.
 * </li></p>
 *
 * @author Zdream
 * @date 2023-12-19
 * @since 0.0.1
 */
public class ChargingBuff extends FoeStageBuff {
	public static final String CATEGORY = MoveAdditionalEffects.E_CHARGING;
	public final StageMove parentMove;

	public float releaseDurationRate;
	public int bonusCount;
	private AttackAction releaseAction;

	// action 放置的时间
	private float startTime, releaseTime;
	private Supplier<Boolean> actionCleaner;

	public ChargingBuff(FoeActor target, StageMove parentMove) {
		super(target);
		this.parentMove = Objects.requireNonNull(parentMove);
	}

	public ChargingBuff withReleaseDurationRate(float releaseDurationRate) {
		this.releaseDurationRate = releaseDurationRate;
		return this;
	}

	public ChargingBuff withRollBonus(int bonusCount) {
		this.bonusCount = bonusCount;
		return this;
	}

	public ChargingBuff withTargetSeat(Seat targetSeat) {
		AttackAction action = new AttackAction(target);
		action.move = parentMove.findSubMove(MoveTemplate.SUB_TITLE_RELEASE);
		action.targetSeat = targetSeat;
		releaseAction = action;
		return this;
	}

	@Override
	public String getCategory() {
		return CATEGORY;
	}

	@Override
	public String toString() {
		return "正在蓄力:" + parentMove.move.name;
	}

	@Override
	public void init() {
		Objects.requireNonNull(releaseAction);
		putReleaseActionToTimeline();
		onHook(HookType.BEFORE_ATTACK_ROLL_CHECK)
				.withPriority(-800)
				.handle(this::putMoreRollsWhenAttack)
				.buildAndSave();
		onHook(HookType.AFTER_EXECUTE_ATTACK)
				.withPriority(1000)
				.handle(this::checkAndRemoveChargingBuff)
				.buildAndSave();
		onHook(HookType.ON_FOE_REFRESH_BSTAT)
				.withPriority(200)
				.handle(this::onFoeSpeedUpdated)
				.buildAndSave();
	}

	public void putReleaseActionToTimeline() {
		startTime = target.stage.now();
		releaseTime = startTime + releaseDurationRate * target.speedCost;

		this.actionCleaner = target.putActionWithTime(releaseAction, releaseTime);
	}

	private void checkAndRemoveChargingBuff(Hooks.HookContext rawContext) {
		Hooks.AttackHookContext context = rawContext.as();
		if (context.attackContext.action != releaseAction) {
			return;
		}

		target.removeBuff(this);
		pushMessage(new FoeRemoveBuffMessage(this));
	}

	private void putMoreRollsWhenAttack(Hooks.HookContext rawContext) {
		Hooks.AttackHookContext context = rawContext.as();
		if (context.attackContext.action != releaseAction) {
			return;
		}

		final AttackContext attackContext = context.attackContext;
		for (int i = 0; i < bonusCount; i++) {
			attackContext.appendMoveRoll(target.createMoreRollWithAttackContext(attackContext, this));
		}
	}

	/**
	 * 当 foe 速度改变时, releaseAction 需要重新放置到 timeline 上
	 */
	private void onFoeSpeedUpdated(Hooks.HookContext rawContext) {
		Hooks.RefreshFoeBStatContext context = rawContext.as();
		if (context.foe != target) {
			return;
		}

		this.actionCleaner.get();
		final float now = target.stage.now();
		final float process = (now - startTime) / (releaseTime - startTime);
		if (process > 1) {
			throw new IllegalStateException("出现了不可能的情况: 蓄力的攻击早应该触发了");
		}

		float updatedCosts = releaseDurationRate * target.speedCost;
		startTime = now - process * updatedCosts;
		releaseTime = startTime + updatedCosts;
		this.actionCleaner = target.putActionWithTime(releaseAction, releaseTime);
	}
}
